home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d20
/
pxsrc21a.arc
/
POLYXARC.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-05-12
|
72KB
|
1,639 lines
/*
PolyXarc: de-archives most archive formats (with the help of the
de-archive programs).
*/
#define R_VERS "2.1a"
#define R_DATE "12 May 1991"
#ifdef AMIGA
#define R_PORT "(Amiga 0.02)"
#endif
#ifdef OS_2
#define R_PORT "(OS/2 1.3)"
#endif
#ifdef MSDOS
#define R_PORT "(MSDOS)"
#endif
/*
-----------------------------------------------------------------------------
Version 2.1a, 12 May 1991: if pak_dosort found a header error, it would return
the same error level as for a fatal error (couldn't open or read the file).
We were leaving the filename intact and exiting instead of renaming to
BAD_ARC and continuing when we were doing bundles. pak_dosort now returns
a 6 for that error.
Now MSC 6.0a compatible (hexit didn't work).
Version 2.1, 5 April 1991: Released DOS and OS/2 versions.
1 January 1991: Will now accept quoted strings and hex numbers in the
SIGNATURE and ARC lines.
- A whitespace character is a space or tab.
- Normally, each non-whitespace character is simply treated as a
character.
- If you place double-quotes (") around a string or part of a string,
that string will be accepted intact, including whitespace.
- If you place a less-than sign (<) in a string, the following characters
will be considered to be 2-digit hex numbers until a greater-than sign
(>) is reached. A hex digit is any numeric digit (0-9) or any letter
from A to F (a-f or A-F). Normally the hex digits will be taken in
pairs, but if any other character is imbedded, it will be considered to
be a number break and the next digit will start the next number.
- If you imbed a backslash ('\'), the backslash will be thrown away and
the next character will be accepted as a character. This allows you
to imbed the double-quote (") and less-than (<) characters as
characters.
10 December 1990: Now sorts .ARC signatures, primarily by ARC version,
secondarily by signature level. This means the user need not worry about
the order, and it simplifies explanation greatly. Added new examples for
PAK 2.51 to POLYXARC.CFG.
18 September 1990: Added support for type 1 ARC headers (see PAKSORTM).
Added support for version 6 headers; version 5 ARC programs can't handle
them. There is now an alternate verb in the config file for version 6
archivers, ARC6. ARC or ARC5 will work for the old stuff.
23 May, 1990: Added a visual indication of the errorlevel at exit to
expedite debugging.
21 May, 1990: Added the -# flag, which tells PolyXarc to ignore any
bundle names that don't end with a decimal digit when using the -F
option. Occasionally a file with a .MOD or some such extension shows
up, and can cause all kinds of havoc. The default is to process them.
Version 2.0, 9 April 1990: Cleaned up some minor stuff. Lattice 'C' on the
Amiga seems to have a problem with leading zeros with the %.3d format, so
we changed it to %0.3d for the Amiga implementation only. Ripped out a
bunch of comments that described the .CFG file in detail; this code is
big enough as it is! Released version 2.0 (finally!). jjn
31 March 1990: Bill Andrus added definitions, DIRFIND.C, and conditional
compilations for IBM C/2 1.1 (or MSC 5.1) under OS/2 with the IBM SDK
Version 1.2 Toolkit. Also insured the MSC conditional works for C/2.
22 March 1990: Steve Palm added definitions and conditional compilations
for Lattice C on the Amiga. I altered some of the stuff to make it even
more portable.
20 February 1990: Changed the way the template works. First I eliminated
the format type. Then I changed the %s specifications to %1, %2, %3, and
%4. Then I added the ability to have more than one file spec on the
command line; everything after the first spec will name a file to be
extracted. So now the four template specs are:
%1: extract string
%2: overwrite string
%3: archive name string
%4: extract name string
Version 1.1a, 3 December 1989: I found another flaw in the bubble sort
routine. jjn
Version 1.1, 2 December 1989: David Page pointed out that SPAZ would
automatically assume a wildcard extension if the archive name was
specified without an extension. I have added this to PolyXarc. jjn
Version 1.0, 2 December 1989: This is the initial release. I now have the
NOSORT verb override the -F switch if it is used. That is, specifying
-F and NOSORT will cause PolyXarc not to sort .ARC files. Also found a
logic error in my bubble sort and fixed it. jjn
Version 0.7, 25 November 1989: Previously, any error would cause PolyXarc
to exit. Now, if it is in -f mode, if the file is 0-length then PolyXarc
will simply delete it; if it gets an error return from the archiver, it
spits out a warning message, renames the file to BAD_ARC.???, and keeps
going. I also changed the config file a bit. jjn
Version 0.6, 21 November 1989: Whoops! this_arc didn't get reset after each
file, so if you unpacked an .ARC file and then something else, it
wouldn't work. Fixed that by resetting this_arc at the same time as
this_signature. Added wildcard support for the non-f mode, which should
have been there in the first place. jjn
Version 0.5, 20 November 1989: Fixed a bug in the -m support; I was
subtracting in the wrong direction. If the configuration file is not
specified on the command line, PolyXarc now looks for the default in
the current directory, then in the directory of the .EXE file, then
looks for a PENGUIN file. jjn
Version 0.4, 18 November 1989: Fixed a bug in the -f support: Turbo C cannot
malloc(0), so I now always malloc() at least one item. jjn
Version 0.3, 18 November 1989: More code cleanup. Removed the extensive
help stuff. Changed from Microsoft C 5.1 to Turbo C 2.0, which went from
96k to 32k (runtime, not .EXE size). Added the runtime configuration
display and -f option support. jjn
Version 0.2, 17 November 1989: Cleaned up some error handling. jjn
Version 0.1, 16 November 1989: Created by Jeffrey J. Nonken
-----------------------------------------------------------------------------
Public domain by Jeffrey J. Nonken, February 1991. No strings, no bullshit.
Do what you want with it. Nobody owns it.
However, I would like it if you would refer all changes and enhancements to
me. This way I can maintain it properly. Thanks.
Jeffrey J. Nonken: sysop of Opus 1:273/715 Blue Bell, Pa. (215)279-9799
My address and phone number will probably change in July, 1991.
Please send correspondence to:
Jeffrey J. Nonken
507 Ave. Presidio
San Clemente, Ca. 92672
This was compiled with Turbo C v2.0.
-----
Steve Palm was not available to help with an Amiga port of 2.1. The following
applies only to version 2.0:
Amiga Correspondence can go to:
Steven M. Palm, sysop of "The Siberian Amiga (MAIL ONLY) 1:11/16"
1000 E. Easterday Ave, #2
Sault Ste. Marie, MI 49783
All thanks and credit for the program belong to Jeffrey, though.
Amiga version compiled under Lattice C 5.05
-----
Bill Andrus of 1:109/301 is responsible for the OS/2 port.
-----
Please note: this source should compile under Lattice C 5.05 on the Amiga,
or Turbo C 2.0 or Microsoft C 5.1 under MSDOS on the IBM. All you need do is
predefine the appropriate label on the command line. However, Turbo C has
a problem with the source files if they have been formatted by the Amiga.
TC requires CRLF newline sequences, where the Amiga only uses LF for newlines.
MSC doesn't seem to have any problem with the Amiga's newline sequences.
It should also compile under IBM C/2 and MSC 6.0a under OS/2.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <dos.h>
#include <errno.h>
#ifdef AMIGA
#define _NEAR_
#endif
#ifdef TURBOC
#include <dir.h>
#include <process.h>
#define _NEAR_
#endif
#ifdef MSC
#include <malloc.h>
#include <memory.h>
#include <process.h>
#define _NEAR_ near
#endif
#ifdef OS_2
#include <process.h>
extern int dir_findfirst(char *filename,int attribute,struct find_t *dta);
extern int dir_findnext(struct find_t *dta);
#endif
#include "p_common.h" /* Portability defines. Must be after */
/* system includes on the Amiga... */
#include "pakmdefn.h" /* Mike Housky's sort function definitions. */
/****************************************************************************/
#ifdef TURBOC
#define bf_name ff_name
#define bf_time ff_ftime
#define bf_date ff_fdate
#define bf_size ff_fsize
#endif
#ifdef AMIGA
#define bf_name filename
#define bf_time time
#define bf_date date
#define bf_size size
#endif
#ifdef MSC
#define bf_name name
#define bf_time wr_time
#define bf_date wr_date
#define bf_size size
#endif
#define OVER_LEN 6 /* Size of overwrite string. */
#define EXTR_LEN 6 /* Size of extract string. */
#define SIGN_LEN 6 /* Size of signature string. */
#define DEFAULT_NAME "POLYXARC.CFG" /* Default name for configuration file. */
typedef struct SIGNATURE_TAG /* 'Signature' structure. */
{
long file_offset; /* Offset from beginning/end of file. */
char overwrite[OVER_LEN]; /* Overwrite parameter string. */
char extract[EXTR_LEN]; /* Extract parameter string. */
char signature[SIGN_LEN]; /* Actual signature string. */
char *cmdline; /* Command line for calling de-archiver. */
} SIGNATURE_TYPE;
typedef struct ARC_TAG /* 'Archive level' structure. */
{
byte type; /* .ARC level */
byte version; /* True if version 6 headers found. */
char overwrite[OVER_LEN]; /* Overwrite parameter string. */
char extract[EXTR_LEN]; /* Extract parameter string. */
char *cmdline; /* Command line for calling de-archiver. */
} ARC_TYPE;
typedef struct FILE_TAG /* Bundle file directory information. */
{
#ifdef AMIGA
char filename[108]; /* currently 30 chars are used */
long time;
long date;
#endif
#ifdef IBM
char filename[13]; /* FILENAME.EXT of file. */
unsigned time; /* Last time updated. */
unsigned date; /* Last date updated. */
#endif
long size; /* Size of the file in bytes. */
} FILE_TYPE;
/****************************************************************************/
FILE_TYPE * _NEAR_ file_list = NULL; /* list of files ('malloc'ed). */
ARC_TYPE * _NEAR_ arc_list;
SIGNATURE_TYPE * _NEAR_ signature_list;
int _NEAR_ this_net; /* Our net number. */
int _NEAR_ this_node; /* Our node number. */
int _NEAR_ config_name_set = 0; /* TRUE if config name specified. */
int _NEAR_ archive_name_set = 0; /* TRUE if archive name specified. */
int _NEAR_ arc_count = 0; /* Number of .ARC formats defined. */
int _NEAR_ signature_count = 0; /* Number of other formats defined. */
int _NEAR_ nosort = 0; /* TRUE if should not sort .ARC files. */
int _NEAR_ delete_archives = 0; /* TRUE if should auto-delete files. */
int _NEAR_ do_all_bundles = 0; /* TRUE if processing all mail bundles. */
int _NEAR_ do_n_bundles = 0; /* Number of archives to do (0 == all). */
int _NEAR_ show_addresses = 0; /* TRUE to display bundle addresses. */
int _NEAR_ this_signature = -1; /* Which signature was found. */
int _NEAR_ this_arc = -1; /* Which arc level was found. */
int _NEAR_ arc = 0; /* TRUE if an .ARC format file found. */
int _NEAR_ overwrite = 0; /* TRUE if automatic overwrite. */
int _NEAR_ quiet = 0; /* TRUE makes PolyXarc quieter. */
int _NEAR_ name_count = 0; /* Counts number of non-switch parameters. */
int _NEAR_ ignore_archiver_errors = 0; /* TRUE continues on archiver errors. */
int _NEAR_ digits_only = 0; /* TRUE forces digits only on bundle names. */
#ifdef AMIGA
extern int errno;
int _NEAR_ msflag = 1; /* causes dir searches to use */
/* MS-DOS wildcards */
#endif
char _NEAR_ archive_name[fname_len] = "";/* Name of archive (from command line). */
char _NEAR_ extract_name[128] = ""; /* Name(s) of file(s) to be extracted
(from command line). */
char _NEAR_ config_name[fname_len] = DEFAULT_NAME; /* Name of configuration file. */
/****************************************************************************
The following define the find_first/find_next environment. The functions that
come with MSC and TURBO C are nearly identical, varying only in some minor
details. 'buffer' is the structure in which the file search routines store
the file information. 'jjn_findfirst' and 'jjn_findnext' are macros that
resolve to the actual calls to the first/next library routines in the two
different libraries. The only differences are the function names, and that two
parameters are swapped in the find_first routine.
****************************************************************************/
#ifdef TURBOC
static struct ffblk _NEAR_ buffer;
#define jjn_findfirst(f,a,b) findfirst(f,b,a)
#define jjn_findnext(b) findnext(b)
#endif
#ifdef AMIGA
struct ffblk {
LONG diskkey;
LONG direntrytype;
char filename[108];
LONG protection;
LONG entrytype;
LONG size;
LONG numblocks;
LONG date;
LONG time;
LONG ticks;
char comment[80];
char reserved[36];
};
static struct ffblk _NEAR_ buffer;
# define jjn_findfirst(f,a,b) dfind((struct FileInfoBlock *) b,(char *) f,(int) a)
# define jjn_findnext(b) dnext((struct FileInfoBlock *) b)
#endif
#ifdef MSC
#ifdef MSDOS
static struct find_t _NEAR_ buffer;
#define jjn_findfirst(f,a,b) _dos_findfirst(f,a,b)
#define jjn_findnext(b) _dos_findnext(b)
#endif
#ifdef OS_2
static struct find_t _NEAR_ buffer;
#define jjn_findfirst(f,a,b) dir_findfirst(f,a,b)
#define jjn_findnext(b) dir_findnext(b)
#endif
#endif
/* This holds the highest compression level found in an .ARC file. */
extern int16 _NEAR_ highest_type;
/* This tells whether we have found version 6 headers. */
extern int16 _NEAR_ version6;
/*****************************************************************************
quit() tells what errorlevel is being returned, then calls exit().
*****************************************************************************/
void quit(int errorlevel)
{
if (!quiet)
printf("\nErrorlevel on exit: %d.\n",errorlevel);
exit(errorlevel);
}
/*****************************************************************************
default_ext() appends a default extension to a filename. Normal operation
happens when the given default extension does not have a period as the first
character. If this is the case, the default extension is only appended if
dest does not already have one. If the given default extension has a period,
it forces the dest to have the default extension even if it already had one.
-----> NOTE: The AmigaDOS system does not require nor expect a filename
to contain an extension. DO NOT USE THIS ROUTINE there.
Examples:
Assume filename is "MYFILE.JNK".
default_ext(filename,"EXT"); [filename is left as "MYFILE.JNK".]
default_ext(filename,".EXT"); [filename is now "MYFILE.EXT".]
Assume filename is "MYFILE".
default_ext(filename,"EXT"); [filename is now "MYFILE.EXT".]
default_ext(filename,".EXT"); [filename is now "MYFILE.EXT".]
*****************************************************************************/
#ifndef AMIGA
char *default_ext(char *dest,char *def_ext)
{
register int i;
for (i = strlen(dest) - 1; i >= 0; --i) /* Look for \, :, or . */
{
if (dest[i] == '.') /* Found a period. Force it? */
{
if (def_ext[0] != '.') /* If none in default ext, forget it. */
{
return(dest); /* Return the given extension. */
}
else
{
strcpy(dest+i,def_ext); /* Otherwise, copy new default ext. */
return(dest); /* Return with a new extension. */
}
}
if (dest[i] == c_slash || dest[i] == ':') /* Found one, no period. */
i = 0; /* Force it to drop through. */
}
/* If it got this far, there was no extension. */
if (def_ext[0] != '.') /* Is there a period in the default ext? */
strcat(dest,"."); /* If not, add one. */
strcat(dest,def_ext); /* Now add the default extension. */
return(dest);
}
#endif
/****************************************************************************
Limit a string to len characters. This routine modifies the original string
and returns the string's address.
****************************************************************************/
char *strtrunc(char *string,int len)
{
register int i; /* Index into the string. */
i = 0; /* Start from the beginning. */
while (string[i] != '\0') /* Do until we reach the end. */
{
if (i == len) /* Did we count len characters? */
{
string[i] = '\0'; /* Yes, truncate the string here. */
return(string); /* Return the truncated string. */
}
++i; /* Not found yet, look farther. */
}
return(string); /* String too short for truncation, return unmodified. */
}
/****************************************************************************
Add a backslash (\) to the end of a path. Only does so if the last character
is not already a backslash or a colon. Note: the Amiga uses a forward slash.
****************************************************************************/
#ifdef IBM
# pragma check_stack-
#endif
void slash(char *pathspec)
{
register int i;
i = strlen(pathspec); /* Find the end of the string. */
if (i--) /* Check for 0 length. If non-zero, backpedal and process it. */
if ((pathspec[i] != c_slash) && /* See if last char is a backslash */
(pathspec[i] != ':')) /* or a colon. */
strcat(pathspec,s_slash); /* If not, add a backslash. */
}
#ifdef IBM
# pragma check_stack+
#endif
/****************************************************************************
Separate drive:directory from name.ext. The original string is not changed.
****************************************************************************/
void path_name(char *filespec,char *path,char *name)
{
register int i;
strupr(filespec); /* Make it upper case. */
for (i = strlen(filespec); i >= 0; --i) /* Start from the end. */
{
if (filespec[i] == ':' || filespec[i] == c_slash) /* '\' or ':'? */
{
++i; /* Yes, point to next char. */
if (name != NULL) /* Copy filename if there is an address. */
strcpy(name,filespec + i);
if (path != NULL) /* Copy file if there is an address. */
{
strncpy(path,filespec,i); /* Copy only i characters. */
path[i] = '\0'; /* Terminate the dest string. */
}
return; /* We got it; now let's return it. */
}
}
/* If we get this far, we never found a colon or backslash. That implies that
the filespec is filename.ext only, no path. */
if (path != NULL) /* Copy empty path if an address was provided. */
strcpy(path,"");
if (name != NULL) /* Copy filename.ext if an address was provided. */
strcpy(name,filespec);
}
/****************************************************************************
This converts a hexASCII digit into a numeric value.
****************************************************************************/
char hexit(char c)
{
c = (char) toupper(c); /* Necessary for MSC 6.0a */
c -= '0';
if (c > 9)
c -= 7;
return(c);
}
/****************************************************************************
This parses off one parameter from the given string and returns its address.
It works similarly to strtok() except that only the string parameter is
specified; the parsing action is fixed.
On the first call per string, pass the string address.
On subsequent calls for that string, pass a NULL pointer.
****************************************************************************/
#define SCANNING 1
#define QUOTE 2
#define HEX1 3
#define HEX2 4
#define FINISHED 5
#define DEADSTOP 6
char *parmtok(char *string,char *tokens)
{
static char *oldstring;
char *newstring;
register int i;
register int j;
register int state;
if (string != NULL)
oldstring = string;
while (strchr(tokens,*oldstring) != NULL)
if (*oldstring == '\0')
return(NULL);
else
++oldstring;
i = 0;
j = 0;
state = SCANNING;
do
{
switch(state)
{
case SCANNING:
if (oldstring[i] == '\0')
state = DEADSTOP;
else if (strchr(tokens,oldstring[i]) != NULL)
state = FINISHED;
else if (oldstring[i] == '"')
state = QUOTE;
else if (oldstring[i] == '\\')
{
if (oldstring[++i] == '\0')
state = DEADSTOP;
else
oldstring[j++] = oldstring[i];
}
else if (oldstring[i] == '<')
state = HEX1;
else
oldstring[j++] = oldstring[i];
break;
case QUOTE:
if (oldstring[i] == '\\')
oldstring[j++] = oldstring[++i];
else if (oldstring[i] == '"')
state = SCANNING;
else if (oldstring[i] == '\0')
state = DEADSTOP;
else
oldstring[j++] = oldstring[i];
break;
case HEX1:
case HEX2:
if (oldstring[i] == '>')
state = SCANNING;
else if (oldstring[i] == '\0')
state = DEADSTOP;
else if (isxdigit(oldstring[i]))
{
if (state == HEX1)
{
oldstring[j] = hexit(oldstring[i]);
state = HEX2;
}
else
{
oldstring[j] = (char) (((byte) oldstring[j] << 4) | (byte) hexit(oldstring[i]));
state = HEX1;
++j;
}
}
else
{
if (state == HEX2)
++j;
state = HEX1;
}
break;
}
++i;
} while (state != FINISHED && state != DEADSTOP);
oldstring[j] = '\0';
newstring = oldstring;
oldstring += (state == DEADSTOP) ? (i - 1) : i;
return(newstring);
}
/****************************************************************************
If the first file timestamp is later than the second, return TRUE.
****************************************************************************/
#ifdef IBM
# pragma check_stack-
#endif
int compare(FILE_TYPE *f1,FILE_TYPE *f2)
{
if (f1->date > f2->date) /* If first file date is later, return TRUE. */
return(1);
if (f1->date < f2->date) /* If first file date is earlier, FALSE. */
return(0);
/* The dates were the same, so try the time. */
if (f1->time > f2->time) /* If first file time is later, return TRUE. */
return(1);
/* The first file is the same as or earlier than the second. In either case,
we don't want to bother swapping, so just return FALSE. */
return(0);
}
#ifdef IBM
# pragma check_stack+
#endif
/****************************************************************************
Find highest BAD_ARC.???, rename current file to the next one.
****************************************************************************/
int ren_bad_arc(char *path,char *bad_name)
{
register int i; /* Highest BAD_ARC.### found so far. */
register int j; /* Holds results from first/next calls. */
register int k; /* Converted BAD_ARC.### value. */
char filespec[fname_len]; /* Filespec of new name. */
/* Also first/next search pattern. */
char bad_spec[fname_len]; /* Complete filespec of bad filename. */
sprintf(filespec,"%sBAD_ARC.???",path); /* Create search pattern. */
i = -1; /* Highest found so far. */
j = jjn_findfirst(filespec, 0, &buffer); /* Find the first one. */
while (j == 0) /* While we keep finding them... */
{
/* Convert the extension to its numeric value. */
k = atoi(strchr(buffer.bf_name,'.') + 1);
if (k > i) /* Is it higher than all the previous found? */
i = k; /* If so, save this one as the highest. */
j = jjn_findnext(&buffer); /* Find the next one, if you can! */
}
/* Gets here when no more can be found. */
/* Create a filespec including the path and BAD_ARC.(i+1). */
#ifdef AMIGA
sprintf(filespec,"%sBAD_ARC.%0.3d",path,i + 1);
#else
sprintf(filespec,"%sBAD_ARC.%.3d",path,i + 1);
#endif
/* Create a filespec including the path and the original filename. */
sprintf(bad_spec,"%s%s",path,bad_name);
/* Tell the operator what is happening. */
printf("Renaming %s to %s.\n",bad_spec,filespec);
/* Rename the file to BAD_ARC.###. */
rename(bad_spec,filespec);
return(i);
}
/****************************************************************************
Find all .SU?, .MO?, .TU?, .WE?, .TH?, .FR?, and .SA? files.
****************************************************************************/
int get_files(char *path,int do_bundles)
{
char *p;
register int i;
register int j;
register int k;
int found;
int ok;
char filespec[64];
static char bundles[7][6] =
{
"*.SU?",
"*.MO?",
"*.TU?",
"*.WE?",
"*.TH?",
"*.FR?",
"*.SA?"
};
FILE_TYPE f;
strcpy(filespec,path); /* Start the filespec out with the path. */
p = filespec + strlen(filespec); /* Point to the end of the path.
This lets us use strcpy() instead
of strcat to concatonate, and
relieves us from having to copy the
path back in every time. */
file_list = (FILE_TYPE *)malloc(sizeof(FILE_TYPE)); /* Allocate 1. */
if (file_list == NULL) /* Check to see if it worked. */
return(-1); /* If not, we have a memory problem. */
i = 0; /* Start index/counter at 0. */
for (j = 0; j < 7; ++j) /* Run through all the extensions. */
{
if (do_bundles) /* If doing all bundles, */
strcpy(p,bundles[j]); /* get the search pattern. */
else /* Otherwise we're just looking for one file; */
j = 7; /* set index for once through. */
if (jjn_findfirst(filespec, 0, &buffer) == 0) /* Find first one. */
{
ok = TRUE;
if (digits_only)
if (!isdigit(buffer.bf_name[strlen(buffer.bf_name) - 1]))
ok = FALSE;
if (ok)
{ /* If we found one, allocate room for more. */
file_list = (FILE_TYPE *)realloc((char *)file_list,(i + 1) * sizeof(FILE_TYPE));
if (file_list == NULL)
return(-1); /* If allocation didn't work, no more memory. */
strcpy(file_list[i].filename,buffer.bf_name); /* Get filename. */
file_list[i].time = buffer.bf_time; /* Get time. */
file_list[i].date = buffer.bf_date; /* Get date. */
file_list[i].size = buffer.bf_size; /* Get size. */
++i; /* Count up. */
}
while (jjn_findnext(&buffer) == 0) /* We're greedy, look for more. */
{
ok = TRUE;
if (digits_only)
if (!isdigit(buffer.bf_name[strlen(buffer.bf_name) - 1]))
ok = FALSE;
if (ok)
{ /* If we found one, allocate room for more. */
file_list = (FILE_TYPE *)realloc((char *)file_list,(i + 1) * sizeof(FILE_TYPE));
if (file_list == NULL)
return(-1); /* If allocation didn't work, no more memory. */
strcpy(file_list[i].filename,buffer.bf_name);/* Get filename. */
file_list[i].time = buffer.bf_time; /* Get time. */
file_list[i].date = buffer.bf_date; /* Get date. */
file_list[i].size = buffer.bf_size; /* Get size. */
++i; /* Count up. */
}
}
}
}
/* We've found all the filenames there are to find. Now we use a bubble sort
to get them all into order by time/datestamp. I used a bubble sort because
the quicksort is much larger, and besides, I don't expect there to be
enough files to make the quicksort time-effective. In fact, I expect that
the files will usually be in order, in which case the bubble sort is
faster because it will only require one pass to find that out. */
if (i > 1) /* Did we find more than one file? */
{ /* Yes, do a bubble sort. */
j = 0; /* Start outside index at 0. */
do
{
found = FALSE; /* This flag says whether any swaps made. */
/* The inside loop starts from the top and goes down to the outside loop
index. That way the lowest gets sorted to the bottom each time, and we
can safely ignore it from then on, and so increment the outside counter. */
for (k = i - 2; k >= j; --k)
{
/* We want the earlier file first. Compare returns TRUE if the earlier one
is second. If they are the same date, we don't care and leave them alone. */
if (compare(&file_list[k],&file_list[k+1]))
{ /* Wrong order, swap the files. */
found = TRUE; /* Trip the flag; forces another pass. */
memcpy((char *)&f,(char *)&file_list[k],sizeof(f));
memcpy((char *)&file_list[k],(char *)&file_list[k+1],sizeof(f));
memcpy((char *)&file_list[k+1],(char *)&f,sizeof(f));
}
}
} while (++j < (i - 1) && found); /* Do until they're in order. */
}
return(i); /* Return the number of files found. */
}
/************************************************************************
Build the command line, based upon template & cmd type
***********************************************************************/
int build_cmd (char *extract_spec, char *overwrite_s, char *extract_s,
char *template_s, char *filespec, char *cmdline)
{
int i, j, percent;
char c, *t;
/* Build the command line according to the template. */
for (i = 0, j = 0, percent = FALSE; template_s[i] != '\0'; ++i)
{
c = template_s[i]; /* Get the character. */
if (percent) /* Was the last one a '%'? */
{
t = NULL; /* Yup. Init this to NULL for testing. */
switch(c) /* Find out which string we want. */
{
case '1': t = overwrite ? "" : extract_s;
break; /* Get the extract string. */
case '2': t = overwrite ? overwrite_s : "";
break; /* Get the overwrite string. */
case '3': t = filespec;
break; /* Get the file spec. */
case '4': t = extract_spec;
break; /* Get the extracted file spec. */
default: cmdline[j++] = c;
break; /* Neither, just copy the character. */
}
if (t != NULL) /* If non-null, we got a string. */
{
strcpy(cmdline + j,t); /* Yup, copy the string in place of the %?. */
j = strlen(cmdline); /* Update the index. */
}
percent = FALSE; /* Reset our '%' flag. */
}
else if (c == '%') /* See if this is a '%'. */
percent = TRUE; /* If so, process next character. */
else
cmdline[j++] = c; /* Else just copy this character. */
}
cmdline[j] = '\0'; /* Append a null. It's now a string. */
return (0);
}
/************************************************************************
Tokenize the command line.
************************************************************************/
int tokenize (char *cmdline, int *argc, char *argv[], char **path)
{
char *t;
/* Now we tokenize the command line. Separate everything at the spaces. I
currently ignore quotes, so this may want some enhancement. */
t = strtok(cmdline," ");
if (t == NULL)
{
printf("Error: encountered empty command line while attempting to tokenize.\n");
return(1); /* No command found! Return an error. */
}
*path = t; /* Store this as the execution path, */
argv[0] = t; /* and as the first argument. */
*argc = 1; /* Set the argument counter/index. */
do
{
t = strtok(NULL," "); /* Get the next token. */
argv[(*argc)++] = t; /* Set it as the next argument and count up. */
} while (t != NULL); /* Do it until we run out of tokens. */
/* The beauty of that scheme is that the argument array is terminated by a
NULL address, which that loop will store as the last argument. */
return(0);
}
/****************************************************************************
Create and execute a command. Don't use COMMAND.COM, we need the result code!
****************************************************************************/
int execute(char *overwrite_s, char *extract_s, char *template_s,
char *filespec, char *extract_spec)
{
char *argv[21]; /* Argument list. Allow 20 arguments + command name. */
char *path; /* Command name. Also stored as argv[0]. */
int argc; /* Number of arguments found. */
int i; /* String index, and result of the spawn. */
char *t; /* String pointer, and token pointer. */
int retval;
#ifdef AMIGA
int offset; /* offset to just past the archiver name in cmd */
char justpath[110];
char justfile[110];
char buffer[256]; /* buffer for my new CMD string */
char cmdline[256]; /* The command string */
struct ProcID pid;
#else
char cmdline[128]; /* The command string. */
#endif
retval = build_cmd (extract_spec, overwrite_s, extract_s, template_s,
filespec, cmdline);
if (!quiet)
printf("-> Executing '%s'\n",cmdline);
if (retval != 0) return (retval);
retval = tokenize (cmdline, &argc, argv, &path);
if (retval != 0) return (retval);
#ifdef AMIGA
/* A Bug in lattice causes the forkv function to NOT search */
/* the C: device for commands. This is why I have to manually */
/* try that combination. Darn irritating! */
i = forkv(path,argv,NULL,&pid); /* Spawn the task. */
if (i == -1) {
path_name (path, justpath, justfile); /* get archiver name */
offset = strlen (path); /* how far past name into cmdline? */
strcpy (buffer, "C:"); /* start with C: path */
strcat (buffer, justfile); /* and add the name on here */
retval = build_cmd (extract_spec, overwrite_s, extract_s, template_s,
filespec, cmdline);
if (retval != 0) return (retval);
strcat (buffer, &cmdline[offset]); /* move the args in */
strcpy (cmdline, buffer); /* now put this all back into cmdline */
retval = tokenize (cmdline, &argc, argv, &path);
if (retval != 0) return(retval);
i = forkv (path, argv, NULL, &pid);
}
#else
i = spawnvp(P_WAIT,path,argv); /* Spawn the task. */
#endif
if (i == -1) /* Did we get an error trying to execute? */
{
switch (errno) /* Yup, figure out why. */
{
case E2BIG:
t = "Argument list too long";
break;
case EINVAL:
t = "Invalid argument";
break;
case ENOENT:
t = "Path or file name too long";
break;
case ENOEXEC:
t = "Exec format error";
break;
case ENOMEM:
t = "Not enough memory";
break;
/* we must put a default in, as Lattice is not properly
setting the errno flag! Thus, I haven't tried to work
around it, I just admit it isn't specified... */
/* Actually, that's a good idea anyway. jjn */
default:
t = "Unspecified Error";
break;
}
printf("Error in execution; reason given: %s.\n",t);
return(-1);
}
#ifdef AMIGA
i = wait(&pid); /* wait for our child process to terminate before
we continue our processing. Ain't multi-tasking
nice??? ;^) */
/* Whaddya think P_WAIT is for in the spawn command? :) */
#endif
return(i); /* No, we managed to execute it. Return the result. */
}
/****************************************************************************
Search through the given file and figure out which type it is.
****************************************************************************/
int find_type(char *path, char *filename, char *filespec)
{
FILE *file; /* File pointer. */
SIGNATURE_TYPE *s; /* Pointer into signature list; provides dereferencing. */
long offset; /* Offset into the file. */
register int i; /* Index into the signature list. */
int l; /* Length of the signature. */
char buffer[SIGN_LEN]; /* Read buffer for the signatures. */
arc = FALSE; /* Note that no .ARC type found. */
file = fopen(filespec,"rb"); /* Open the file for binary read. */
if (file == NULL) /* If can't open it, return with error. */
{
printf("Error: cannot open %s.\n",filespec);
return(1);
}
this_arc = -1; /* Show that nothing found so far. */
this_signature = -1;
i = 0; /* Start index at first signature. */
offset = 0xffffffff; /* Show a ridiculous offset at first. */
while (i < signature_count && this_signature == -1)
{
s = &signature_list[i]; /* Dereference this signature. */
l = strlen(s->signature); /* Get the signature length. */
if (s->file_offset != offset) /* See if we already have that spot. */
{
offset = s->file_offset; /* Nope, move to it and read it. */
fseek(file,offset,(offset < 0) ? 2 : 0);
fread(buffer,sizeof(buffer),1,file);
}
if (strncmp(s->signature,buffer,l) == 0) /* Are they the same? */
/* Note: case sensitive! */
this_signature = i; /* If so, record it. */
++i; /* Increment the counter. */
} /* Do that until we find a signature or run out. */
if (this_signature == -1) /* Did we find a signature? */
{ /* If not, look for an .ARC file. */
if (offset != 0l) /* See if we have this in memory. */
{
fseek(file,0l,0); /* If not, seek 0 and read 1 byte. */
fread(buffer,1,1,file);
}
if (buffer[0] == 0x1a) /* 1A is the .ARC signature. */
arc = TRUE; /* If so, assume it's an .ARC file. */
}
fclose(file); /* Either way, we're done for now. */
if (arc) /* Is it an .ARC file? */
{
i = pak_dosort(path,filename,!nosort); /* If so, try to sort. */
printf("Highest ARC level found: %d.\n",highest_type);
/*
Actually, pak_dosort() scans through the .ARC file, finding the highest
level of compression used. It only sorts if nosort is FALSE.
Returns 0 if successful, or error:
1 = Can't open/read source. (missing pakfile or DOS error)
2 = Can't create/write dest. (maybe disk or directory full)
3 = Can't delete old source file. (read-only)
4 = Can't rename temp file to source name. (DOS error)
5 = Out of memory.
6 = Invalid pakfile. (Header error)
If we got 2, print a warning and go on. Otherwise, we quit on any error.
*/
if (i == 2)
{
printf("Warning: sort failed. Attempting extract anyway.\n");
}
else if (i != 0)
return(i);
/* Did ok, so let's see what we've got. */
i = 0;
while (i < arc_count && this_arc == -1)
{
if ((version6 && arc_list[i].version) || !version6)
if ((byte)highest_type <= arc_list[i].type)
this_arc = i; /* Set this_arc to .ARC type found. */
/* If the highest .ARC type is higher than we can handle, it never finds it.
The calling routine will claim that we couldn't find the archive type.
If the archive has version 6 headers, then the extractor must also be
capable of version 6 extraction. */
++i;
}
}
return(0);
}
/****************************************************************************
Converts the first 4 characters from a string from hex to a signed integer.
If there are any non-digits in those four characters, it returns -65536.
****************************************************************************/
long one_hex(char *name)
{
int i; /* Index. */
register int16 j; /* Accumulated result. */
register int16 c; /* Holding place for the converted character. */
j = 0; /* Reset accumulated result. */
for (i = 0; i < 4; ++i) /* Go through the first 4 characters. */
{
c = toupper(name[i]); /* Get the upper case character. */
if (!isxdigit(c)) /* Is it a hex digit? */
return -65536; /* If not, return error. */
if (c >= 'A') /* Change 'A'-'F' into A-F. */
c -= 7;
j = (j << 4) + c - '0'; /* Subtract ASCII offset and accumulate. */
}
return (long)j; /* Return the result. */
}
/****************************************************************************
Display address of sender.
****************************************************************************/
void sender_is(char *filename)
{
int net;
int node;
long i;
i = one_hex(filename); /* Convert first 4 characters of filename. */
if (i == -65536) /* If not hex number, ignore it. */
return;
net = (int16)i; /* Assume this is a net difference. */
i = one_hex(filename + 4); /* Convert last 4 characters of filename. */
if (i == -65536) /* If not hex number, ignore it. */
return;
node = (int16)i; /* Assume this is a node difference. */
/* Calculate the net/node address from the filename and your address, and
display the result. */
printf("Extracting bundle from %u/%u.\n",this_net + net,this_node + node);
}
/****************************************************************************
Discombobulate one file.
****************************************************************************/
int discombobulate(char *path,char *filename, char *extract_spec)
{
int i; /* General purpose variable. */
char filespec[fname_len]; /* File specification generated from path and name. */
if (show_addresses) /* If we're supposed to show the address, do so. */
sender_is(filename);
strcpy(filespec,path); /* Create the filespec from the path */
strcat(filespec,filename); /* and the filename. */
i = find_type(path,filename,filespec); /* Find the type of file. */
if (i) /* If can't do it, return with the error. */
return(i);
if (this_arc != -1) /* See if it's an .ARC file. */
{
i = execute(arc_list[this_arc].overwrite,
arc_list[this_arc].extract,
arc_list[this_arc].cmdline,
filespec,
extract_spec); /* Try to extract. */
}
else if (this_signature != -1) /* Nope, see if it's something else. */
{
i = execute(signature_list[this_signature].overwrite,
signature_list[this_signature].extract,
signature_list[this_signature].cmdline,
filespec,
extract_spec); /* Try to extract. */
}
else /* Whoops! We couldn't figure it out. */
{
printf("Error: cannot determine archive type of %s.\n",filespec);
if (arc && this_arc == -1)
printf("The ARC level was higher than anything defined.\n");
return(6);
}
if (i > 0) /* This means the archiver ran into trouble. */
{
printf("Error return %d from archiver.\n",i);
i = 8;
}
else if (i < 0) /* This means we never got to execute it. */
{
return(10);
}
else if (delete_archives) /* No problems. Erase the evidence. */
{
printf("Unpack successful: deleting %s.\n",filespec);
i = unlink(filespec);
if (i)
{
printf("Error: could not delete %s.",filespec);
#ifdef OS_2
printf("\nERRNO is %d.",errno);
#endif
i = 3;
}
}
printf("\n");
return(i);
}
/****************************************************************************
Discombobulate all bundles (or n bundles if do_n_bundles != 0).
****************************************************************************/
int discombobulate_all(char *path, char *extract_spec, int do_bundles)
{
int numfiles; /* How many files we found. */
int count; /* Index counter. */
int i; /* General purpose register. */
numfiles = get_files(path,do_bundles); /* Collect filenames. */
if (numfiles < 0) /* < 0 means we got an error in allocation. */
return(5);
/* Not having anything to discombobulate with the -F switch isn't _really_ an
error condition. Having PolyXarc return an error under these circumstances
made me and Peter Stern spend several days beating our heads against the
wall. From now on we'll just return 0 if this happens. -jjn */
if (numfiles == 0) /* == 0 means no files found. */
return(do_all_bundles ? 0 : 1);
if (do_n_bundles) /* If limited, */
if (numfiles > do_n_bundles) /* and if past the limit, */
numfiles = do_n_bundles; /* limit the file count. */
/* If we're not doing bundles, then path was actually a filespec. In that case
we need to throw away the filename.ext and keep the path. The filename.ext
will be drawn from the list of files that we got from get_files(). */
if (!do_bundles)
path_name(path,path,NULL);
for (count = 0; count < numfiles; ++count) /* Do all the files. */
{
if (file_list[count].size == 0l) /* Is this an empty file? */
{
if (do_bundles) /* Are we doing bundles? */
{
unlink(file_list[count].filename); /* Yes; just delete it. */
printf("Zero-length file %s deleted.",file_list[count].filename);
}
else /* No, consider this an error. */
{
printf("Error: file %s is length 0.",file_list[count].filename);
return(9);
}
}
else /* The file is not zero length. */
{
i = discombobulate(path,file_list[count].filename,extract_spec);
/* Extract. */
if (do_bundles) /* Are we doing bundles? */
{
switch (i) /* If so, look at the return value. */
{
case 0: /* Nothing went wrong, do nothing. */
break;
case 1:
case 2: /* Fatal error, return error code. */
case 4:
case 5:
case 7:
return(i);
default: /* Non-fatal, rename file and continue. */
ren_bad_arc(path,file_list[count].filename);
}
}
else /* Not doing bundles. If we got an error, just return it. */
{
if (i == 8 && ignore_archiver_errors)
i = 0;
else
if (i)
return(i);
}
}
}
return(0); /* No fatal errors. Return. */
}
/****************************************************************************
Find "PENGUIN=" in environment
Unfortunately, under Amiga Lattice the 'getenv()' function only returns
Commodore's Environment variables, *NOT* the more common Manx/ARP/Rockiki
style variables... I will try to remedy this in the future.
****************************************************************************/
int p_env(void)
{
char *t;
t = getenv("PENGUIN"); /* Try to find a PENGUIN file. */
if (t != NULL) /* See if "PENGUIN=" in environment. */
{
strcpy(config_name,strupr(t)); /* Get the name of the file. */
return(1); /* Return TRUE. */
}
return(0); /* Nothing found, return FALSE. */
}
/****************************************************************************
Process the configuration file.
The configuration file consists of one or more blocks that start with
BEGIN POLYXARC and end with END POLYXARC (case insensitive). Between the
delimiting statements are one or more keywords, one per line. Most keywords
will have parameters sharing the line. Keywords are:
ARC level overwrite extract extract_command_template
%1: extraction string
%2: overwrite string
%3: archive file spec
%4: extracted file spec
SIGNATURE offset signature overwrite extract extract_command_template
%1: extraction string
%2: overwrite string
%3: archive file spec
%4: extracted file spec
NOSORT
****************************************************************************/
int get_config(char *argv0)
{
FILE *config=NULL; /* Config file. */
char *f; /* Scratch string */
char *g; /* Scratch string */
char *s1; /* Pointer to signature token. */
char *s2; /* Pointer to overwrite token. */
char *s3; /* Pointer to extract token. */
char oneline[132]; /* Holds one line from file. */
register int i; /* scratch integer */
int quit;
int arc6; /* True if found version 6 definition. */
long l;
if (config_name_set || !(config_name == NULL))
config = fopen(config_name,"rt"); /* Open the config file. */
if (config == NULL && !config_name_set) /* Did it work? */
{ /* Nope. */
path_name(argv0,config_name,NULL); /* Get path of POLYXARC.EXE. */
strcat(config_name,DEFAULT_NAME); /* Add the default .CFG name. */
config = fopen(config_name,"rt"); /* Now try to open it. */
if (config == NULL) /* Did that work? */
{ /* Nope. */
/* Set up the config name to default to the default name in the current
directory, then try to find a PENGUIN file. If there is a penguin file,
it overrides the default. Otherwise, we'll try the default. */
strcpy(config_name,DEFAULT_NAME); /* Restore the default. */
if (p_env()) /* Get PENGUIN file, if any. */
config = fopen(config_name,"rt");/* Try to open it. */
}
}
if (config == NULL) /* Nothing worked! */
{
printf("Cannot open configuration file %s.\n",config_name);
return(1);
}
arc_list = /* Pre-allocate 1. */
(ARC_TYPE *)malloc(sizeof(ARC_TYPE));
signature_list = /* Pre-allocate 1. */
(SIGNATURE_TYPE *)malloc(sizeof(SIGNATURE_TYPE));
do /* Do until we run out of file. */
{
i = TRUE; /* Initialize this flag. */
do /* Do until i == FALSE. */
{
f = fgets(oneline,sizeof(oneline),config); /* Get one line. */
if (f == NULL) /* Did it work? */
i = FALSE; /* If not, drop through. */
else
{
if ((g = parmtok(oneline," \t\n")) != NULL)/* Tokenize the line. */
{
if (stricmp(g,"BEGIN") == 0)/* First word BEGIN? */
{
g = parmtok(NULL," \t\n"); /* If so, tokenize again. */
if (stricmp(g,"POLYXARC") == 0) /* Second word POLYXARC? */
i = FALSE; /* If so, drop through. */
}
}
}
} while (i); /* Do this until i == FALSE. */
quit = 0; /* Don't quit yet! */
do /* Do until quit. */
{
f = fgets(oneline,sizeof(oneline),config); /* Get a line. */
if (f != NULL) /* Did it work? */
{ /* Yup. */
if ((g = parmtok(oneline," \t\n")) != NULL)/* Tokenize. */
{
/* ARC level command_template */
if (strnicmp(g,"ARC",3) == 0) /* First word ARC? */
{ /* Yup. */
arc6 = ('6' == g[3]);
i = atoi(parmtok(NULL," \t\n"));/* Get ARC level. */
s2 = parmtok(NULL," \t\n"); /* Get overwrite switch. */
s3 = parmtok(NULL," \t\n"); /* Get extract switch. */
g = parmtok(NULL,"\n"); /* Get the template. */
if (g == NULL) /* Did we get all those fields? */
{ /* Nope, something was left out. */
printf("Syntax error in config file.\n");
return(7);
}
while (*g == ' ' || *g == '\t') /* Remove leading spaces. */
++g;
arc_list = /* Allocate room for more. */
(ARC_TYPE *)realloc(arc_list,
(arc_count + 1) * sizeof(ARC_TYPE));
arc_list[arc_count].type = (byte)i; /* Save ARC level. */
arc_list[arc_count].version = (byte)arc6;/* Record if version 6. */
strcpy(arc_list[arc_count].overwrite, /* Save overwrite. */
strtrunc(s2,OVER_LEN - 1));
strcpy(arc_list[arc_count].extract, /* Save extract. */
strtrunc(s3,EXTR_LEN - 1));
arc_list[arc_count].cmdline = /* Allocate for template. */
malloc(strlen(g) + 1);
strcpy(arc_list[arc_count].cmdline,g); /* Save template. */
++arc_count; /* Count up. */
}
/* SIGNATURE offset signature command_template */
else if (stricmp(g,"SIGNATURE") == 0) /* First word SIGNATURE? */
{ /* Yup. */
l = atol(parmtok(NULL," \t\n"));/* Get ARC level. */
s1 = parmtok(NULL," \t\n"); /* Get signature string. */
s2 = parmtok(NULL," \t\n"); /* Get overwrite switch. */
s3 = parmtok(NULL," \t\n"); /* Get extract switch. */
g = parmtok(NULL,"\n"); /* Get the template. */
if (g == NULL) /* Did we get all those fields? */
{ /* Nope, something was left out. */
printf("Syntax error in config file.\n");
return(7);
}
while (*g == ' ' || *g == '\t') /* Remove leading spaces. */
++g;
signature_list = /* Allocate room for more. */
(SIGNATURE_TYPE *)realloc(signature_list,
(signature_count + 1) * sizeof(SIGNATURE_TYPE));
signature_list[signature_count].file_offset = l;/* Save offset. */
strcpy(signature_list[signature_count].signature,/* Save signature. */
strtrunc(s1,SIGN_LEN - 1));
strcpy(signature_list[signature_count].overwrite,/* Save overwrite. */
strtrunc(s2,OVER_LEN - 1));
strcpy(signature_list[signature_count].extract, /* Save extract. */
strtrunc(s3,EXTR_LEN - 1));
signature_list[signature_count].cmdline = /* Allocate for template. */
malloc(strlen(g) + 1);
strcpy(signature_list[signature_count].cmdline,g); /* Save template. */
++signature_count; /* Count up. */
}
else if (stricmp(g,"NOSORT") == 0) /* First word NOSORT? */
{ /* Yup. */
nosort = TRUE; /* Override sort directive. */
}
else if (stricmp(g,"END") == 0) /* First word END? */
{ /* Yup. */
g = parmtok(NULL," \t\n"); /* Tokenize again. */
if (stricmp(g,"POLYXARC") == 0) /* Second word POLYXARC? */
quit = 1; /* Yup. Ignore everything
until end of file or
another BEGIN POLYXARC. */
}
}
}
else
quit = 2; /* Reached end of file, really quit now. */
} while (!quit); /* Do until quit. */
} while (quit < 2); /* If quit == 1, not end of file, just end of
BEGIN/END block. Keep looking. */
fclose(config); /* End of file, close it. */
return(0); /* No errors, return 0. */
}
/****************************************************************************
This sorts the .ARC list by type.
****************************************************************************/
void sort_arc_list(void)
{
ARC_TYPE local_arc_list;
register int i;
register int j;
register int k;
register int swapped;
k = arc_count - 1;
do
{
swapped = 0;
for (i = 0; i < k; ++i)
{
j = arc_list[i].version - arc_list[i+1].version;
if (j > 0 || (j == 0 && arc_list[i].type > arc_list[i+1].type))
{
memcpy((char *)&local_arc_list,(char *)&arc_list[i],sizeof(ARC_TYPE));
memcpy((char *)&arc_list[i],(char *)&arc_list[i+1],sizeof(ARC_TYPE));
memcpy((char *)&arc_list[i+1],(char *)&local_arc_list,sizeof(ARC_TYPE));
swapped = 1;
}
}
--k;
} while (swapped);
}
/****************************************************************************
Process command-line parameters
-# Does not recognize a .MO? - .SU? file unless it ends
with a decimal digit. Used only with the -F flag.
-Cconf Specify configuration file. Default is POLYXARC.CFG.
-D Will delete the archive if the extract was successful.
-F[n] Will process all Compressed Mail bundles found in Dir.
-Maddr Will calculate Compressed Mail bundle name to show the
sending Net Address. "addr" is YOUR Net/Node address.
-N Will NOT attempt to sort the archive prior to extract.
-O Overwrite. Will NOT prompt if existing file is found.
-R Same as -O, included for ARCE syntax compatibility.
-Q QuietMode. Will NOT display the runtime configuration.
If -F is used then "Archive" MUST be a Path ONLY, not a Filename.
Use of -F forces -D and -O to be set TRUE and -N to be set False.
(The NOSORT verb in the configuration file overrides the -F parameter
and forces -N to be set True.) If a number is specified with the
-F parameter, then PolyXarc will only process that number of archives
(as a maximum).
****************************************************************************/
void process_args(char *arg)
{
char *tok;
tok = strtok(arg,"\n");
if (tok == NULL)
return;
if (*tok == '/' || *tok == '-')
{
++tok;
switch(toupper(*tok))
{
case '#': digits_only = TRUE;
break;
case 'C': strcpy(config_name,strupr(++tok));
config_name_set = TRUE;
break;
case 'D': delete_archives = TRUE;
break;
case 'F': do_all_bundles = TRUE;
do_n_bundles = atoi(++tok);
break;
case 'I': ignore_archiver_errors = TRUE;
break;
case 'M': show_addresses = TRUE;
this_net = (unsigned int)atol(strtok(++tok,"/."));
this_node = (unsigned int)atol(strtok(NULL,"/."));
break;
case 'N': nosort = TRUE;
break;
case 'O':
case 'R': overwrite = TRUE;
break;
case 'Q': quiet = TRUE;
break;
}
}
else
{
switch(name_count++)
{
case 0: strcpy(archive_name,strupr(tok)); /* Get archive filespec. */
archive_name_set = TRUE;
break;
case 1: strcpy(extract_name,tok); /* Get extract name filespec. */
break;
default: strcat(extract_name," "); /* Add a space for parameter separation. */
strcat(extract_name,tok); /* Get another extract name filespec. */
break;
}
}
}
/****************************************************************************
Display the run-time configuration.
****************************************************************************/
void display_configuration(void)
{
printf("Archive %s is %s.\n",
do_all_bundles ? "path" : "name",
archive_name_set ? (char *)archive_name : "the current directory");
printf("Using configuration file %s.\n",config_name);
printf("%s delete archives after extraction.\n",
delete_archives ? "Will" : "Will not");
if (do_all_bundles)
{
if (do_n_bundles)
printf("Will process up to %d mail archives.\n",do_n_bundles);
else
printf("Will process all mail archives.\n");
}
printf("%s show source addresses.\n",show_addresses ? "Will" : "Will not");
if (show_addresses)
printf("This node's address is %u/%u.\n",this_net,this_node);
printf("%s sort .ARC format archives.\n",nosort ? "Will not" : "Will");
printf("%s automatically overwrite existing files.\n\n",
overwrite ? "Will" : "Will not");
if (ignore_archiver_errors)
printf("Will ignore errors from the archiver(s).\n");
}
/****************************************************************************
Main function.
Returns 0 if successful, or errorlevel for DOS on error:
1 = Can't open/read source. (missing or invalid file)
2 = Can't create/write dest (maybe disk or directory full)
3 = Can't delete old source file. (read-only)
4 = Can't rename temp file to source name.
5 = Out of memory.
6 = Cannot determine archive type.
7 = Configuration file syntax error.
8 = Non-0 error return from archive extractor.
9 = Zero-length file.
10 = Error while attempting to execute the archiver.
****************************************************************************/
int main(int argc,char **argv)
{
int i;
printf("\nPolyXarc v" R_VERS " " R_PORT " " R_DATE "; Public Domain.\n");
printf("Written by Jeffrey Nonken of 1:273/715@Fidonet (215)279-9799.\n\n");
#ifdef AMIGA
printf("Ported to the Amiga by Steven M. Palm. 1:11/16 @ FidoNet\n");
printf("Subject to change without notice. No support implied.\n");
printf("Just a reminder: This uses MS-DOS style wildcards.\n\n");
#endif
#ifdef OS_2
printf("Ported to OS/2 by Bill Andrus of 1:109/301@Fidonet.\n\n");
#endif
if (argc == 2) if (stricmp(argv[1],"?") == 0)
{
printf("PolyXarc archive [-Cconf] [-D] [-F[n]] [-#] [-Maddr] [-N]\n"
" [-O|R] [-Q] [-I] [filename [filename...]]\n"
"See POLYXARC.DOC for more details.\n");
quit(0);
}
for (i = 1; i < argc; ++i) /* Process command-line arguments. */
process_args(argv[i]);
if (do_all_bundles) /* If -f, override some settings. */
{
delete_archives = TRUE; /* Delete archives when done. */
overwrite = TRUE; /* Overwrite existing files. */
nosort = FALSE; /* Don't sort. */
slash(archive_name); /* Add trailing backslash to path. */
}
#ifndef AMIGA
else /* I said we don't do default extensions under AmigaDOS... ;^) */
default_ext(archive_name,"*"); /* If no extension, add ".*". */
#endif
i = get_config(argv[0]); /* Get the configuration file. */
if (!i) /* If it worked, keep going. */
{
sort_arc_list(); /* Sort the .ARC list by level. */
if (!do_all_bundles /* Doing bundles? */
&& !archive_name_set) /* If not, needs a filename. */
{ /* No file name specified! Return error. */
printf("Error: archive name not specified. Use 'PolyXarc ?' for help.\n");
quit(1);
}
if (!quiet) /* If no -q, display runtime config. */
display_configuration();
i = discombobulate_all(archive_name,extract_name,do_all_bundles);
}
fcloseall(); /* Close all files. */
#ifdef AMIGA
quit(i); /* for some reason, return() will not function as the */
/* exit the main function prperly under Lattice 5.05. */
/* It always gives 0. I don't s'pose it would hurt to */
/* do this on the IBM set as well.... */
#endif
/* Actually, you get a warning from the MSC compiler if you don't have
a return statement in a function. I decided to leave the return in for
MSC and TURBOC. jjn */
#ifdef IBM
quit(i);
return(i);
#endif
}